/*
 *  Copyright (C) 2010 Francesco Boffa (aka AlfaOmega08)
 * 
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 * 
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 * 
 *  You should have received a copy of the GNU General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package Engine;

public class Memory {
	byte[] StdMem = new byte[0x200000];
	byte[] ParMem = new byte[0x10000];
	byte[] HarMem = new byte[0x10000];
	byte[] RomMem = new byte[0x80000];

	long Read(long pos, int cnt) throws Exception {
		pos &= 0xFFFFFFFF;
		int topNibble = ((int) pos >> 28) & 0xF;

		if (cnt != 1 && cnt != 2 && cnt != 4)
			throw new Exception("Invalid count");

		switch (topNibble) {
			case 0x0:
			case 0x8:
			case 0xA:
				return ReadMem(StdMem, (int) (pos & 0x1FFFFF), cnt);
			case 0xB:
				if ((pos & 0xFFF00000) != 0xBFC00000) {
					return 0;
				}
				return ReadMem(RomMem, (int) (pos & 0x7FFFF), cnt);
			case 0x1:
				if ((pos & 0xFF00000) == 0xF00000) {
					return ReadMem(ParMem, (int) (pos & 0xFFFF), cnt);
				} else if ((pos & 0xFF00000) == 0xF80000) {
					if ((pos & 0xFFFF) < 0x1000) {
						return ReadMem(HarMem, (int) (pos & 0xFFF), cnt);
					} else {
						System.out.println("Reading from hardware still unsupported");
						return 0;//ReadHw(pos & 0x2FFF, cnt);
					}
				}
				return 0;
			default:
				return 0;
		}
	}

	void Write(long pos, long val, int cnt) throws Exception {
		pos &= 0xFFFFFFFF;
		int topNibble = ((int) pos >> 28) & 0xF;

		if (cnt != 1 && cnt != 2 && cnt != 4)
			throw new Exception("Invalid count");

		switch (topNibble) {
			case 0x0:
			case 0x8:
			case 0xA:
				WriteMem(StdMem, (int) (pos & 0x1FFFFF), val, cnt);
				return;
			case 0xB:
				/* ROM can't be written by software */
				System.out.println("Attempt to write in ROM memory");
				return;
			case 0x1:
				if ((pos & 0xFF00000) == 0xF00000) {
					WriteMem(ParMem, (int) (pos & 0xFFFF), val, cnt);
				} else if ((pos & 0xFF00000) == 0xF80000) {
					if ((pos & 0xFFFF) < 0x1000) {
						WriteMem(HarMem, (int) (pos & 0xFFF), val, cnt);
					} else {
						System.out.println("Write to hardware still unsupported");
						return;//WriteHw(pos & 0x2FFF, val, cnt);
					}
				}
				return;
			default:
				return;
		}
	}

	/* Java is annoying as it do not support unsigned types. We must use long to represent an unsigned int */
	public long ReadMem(byte[] MemArray, int pos, int cnt) {
		long Ret = 0;

		switch (cnt) {
			case 1:
				Ret |= MemArray[pos] & 0xFF;
				break;
			case 2:
				Ret |= MemArray[pos] & 0xFF;
				Ret |= (MemArray[pos + 1] & 0xFF) << 8;
				break;
			case 4:
				Ret |= MemArray[pos] & 0xFF;
				Ret |= (MemArray[pos + 1] & 0xFF) << 8;
				Ret |= (MemArray[pos + 2] & 0xFF) << 16;
				Ret |= (MemArray[pos + 3] & 0xFF) << 24;
				break;
			default:
				break;
		}

		return Ret;
	}

	public void WriteMem(byte[] MemArray, int pos, long val, int cnt) {
		switch (cnt) {
			case 1:
				val &= 0xFF;
				MemArray[pos] = (byte) (val & 0xFF);
				break;
			case 2:
				val &= 0xFFFF;
				MemArray[pos] = (byte) (val & 0xFF);
				MemArray[pos + 1] = (byte) ((val >> 8) & 0xFF);
				break;
			case 4:
				val &= 0xFFFFFFFF;
				MemArray[pos] = (byte) (val & 0xFF);
				MemArray[pos + 1] = (byte) ((val >> 8) & 0xFF);
				MemArray[pos + 2] = (byte) ((val >> 16) & 0xFF);
				MemArray[pos + 3] = (byte) ((val >> 24) & 0xFF);
				break;
			default:
				break;
		}
	}

	public byte[] getRomMemory() {
		return RomMem;
	}
}
